package service.sheetmanager;

import java.util.ArrayList;
import java.util.List;
import javax.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.validation.Errors;
import service.configuration.IBlockConfig;
import service.configuration.IConfiguration;
import service.configuration.IFieldConfig;
import service.configuration.ISheetConfig;
import service.configuration.NamingUtil;
import service.normalization.INormalizer;
import service.validation.ISheetValidator;
import web.form.Block;
import web.form.Item;
import web.form.Link;
import web.form.LinkField;
import web.form.SheetInfo;
import web.form.SheetView;
import dao.IDaoAsof;
import entities.LinkAssociation;
import entities.Sheet;
import entities.Status;


@Service
public class SheetManager implements ISheetManager {

	private static final boolean linkMe = true;
	
	@Resource
	private IConfiguration conf;
	
	@Resource
	private IDaoAsof dao;
	
	@Resource
	private ISheetValidator sheetValidator;
	
	@Resource
	private INormalizer normalizer;
	

	/*
	private Map<String, SheetInfo> sheetInfoByCode;
	//@PostConstruct
	private void init() {
		sheetInfoByCode = new TreeMap<String, SheetInfo>();
		for(ISheetConfig sheetConf : conf.getAllSheetConfigs()) {
			for(Sheet sheet : dao.findAllSheets(sheetConf.getName())) {
				sheetInfoByCode.put(sheet.getCode(), (generateSheetInfo(sheet)));
			}
		}
	}
	*/
	
	/**
	 * Contruction d'une SheetInfo à partir d'une Sheet
	 * 
	 * @param sheet Sheet
	 * @return la SheetInfo construite
	 */
	public SheetInfo generateSheetInfo(Sheet sheet) {
		return new SheetInfo(sheet.getType(), sheet.getCode(), 
				generateSheetTitle(sheet), 
				sheet.getStatus() == Status.Valid);
	}
	
	/**
	 * Contruction d'une chaine de caractère corrspondant au titre d'une fiche 
	 * à partir d'une Sheet
	 * 
	 * @param sheet Sheet
	 * @return le titre construit
	 */
	public String generateSheetTitle(Sheet sheet) {
		String title = "";
		for(String fieldKey : conf.getShortDisplayFieldKeys(sheet.getType())) {
			if (sheet.getProperty(fieldKey) != null) {
				title += sheet.getProperty(fieldKey) + " ";
			}
		}
		return title.trim();
	}
	
	/**
	 * Contruction d'une SheetView à partir d'une Sheet
	 * 
	 * @param sheet Sheet
	 * @return la SheetView construite
	 */
	public SheetView generateSheetView(Sheet sheet) {

		if (sheet == null) {
			return null;
		}
		
		ISheetConfig sheetConf = conf.getSheetConfig(sheet.getType());
		if (sheetConf == null) {
			return null;
		}
			
		List<Block> blocks = new ArrayList<Block>();
		for(IBlockConfig blockConf : sheetConf.getBlockConfigs()) {
			
			List<Item> items = new ArrayList<Item>();
			for(IFieldConfig fieldConf : blockConf.getFieldConfigs()) {
				if (! fieldConf.isHidden()) {
					items.add(new Item(fieldConf.getName(), fieldConf.getLabel(), fieldConf.getInfo(), 
							           fieldConf.isReadOnly(), !fieldConf.isOptional(),
							           (String)sheet.getProperty(fieldConf.getName()), fieldConf.getType()));
				}
			}
			
			blocks.add(new Block(blockConf.getLabel(), items));
		}
		
		// Ajout des liens
		if (linkMe) {
			List<Link> links = new ArrayList<Link>();
			for (String label : dao.findLinkAssociationLabels(sheet.getCode(), sheet.getType())) {
				List<LinkAssociation> linkAssociations = dao.findLinkAssociationByLabel(
						sheet.getCode(), sheet.getType(), label);
				
				if (linkAssociations.isEmpty()) {
					continue;
				}
				
				String toType = linkAssociations.get(0).getToType();
				
				List<String> linkCodes = new ArrayList<String>(linkAssociations.size());
				for(LinkAssociation linkassociation : linkAssociations) {
					linkCodes.add(linkassociation.getToCode());
				}
				List<Sheet> linkSheets = dao.findSheetsByProperty(NamingUtil.CODE, linkCodes, toType);
				List<LinkField> linkFields = new ArrayList<LinkField>(linkAssociations.size());
				for (Sheet linkSheet : linkSheets) {
					linkFields.add(new LinkField(generateSheetTitle(linkSheet), linkSheet.getCode()));
				}
				links.add(new Link(label, toType, linkFields));
			}
			if (! links.isEmpty() && ! links.get(0).getFields().isEmpty()) {
				blocks.add(new Block("Liens", null, links));
			}
		}
		// Fin ajout des liens
		
		SheetView sheetView = new SheetView(blocks, 
				sheet.getCode(), sheet.getType(), sheetConf.getLabel(), 
				generateSheetTitle(sheet), sheet.getStatus());
		
		return sheetView;
	}

	
	private void fillSheet(SheetView view, Sheet sheet) {
		if (sheet == null || view == null || view.getBlocks() == null)
			return;
		for(Block block : view.getBlocks()) {
			for(Item item : block.getItems()) {
				if (! item.isReadOnly()) {
					sheet.setProperty(item.getKey(), item.getValue());
				}
			}
		}
		sheet.setStatus(view.getStatus());
		
	}

	/**
	 * Persiste une SheetView si celle-ci n'est pas en erreur dure 
	 * et renvoie les erreurs douce et dure.
	 * 
	 * @param view Sheet
	 * @param errors pour lier les erreurs au champs correspondant
	 * @return le status de la SheetView
	 */
	public Status updateSheet(SheetView view, Errors errors) {
		if (view == null || errors == null)
			return Status.HardError;
		
		normalizer.normalizeSheetView(view);
		sheetValidator.validate(view, errors);
		if (view.getStatus() != Status.HardError) {
			try {
				Sheet sheet = dao.findSheetByCode(view.getCode(), view.getType());
				fillSheet(view, sheet);
				dao.updateSheet(sheet);
				view.setTitle(generateSheetTitle(sheet));
			} catch(Exception e) {
				throw new SheetNotFoundException();
			}
		}
		return view.getStatus();
	}

	/**
	 * Recherche une fiche dans la base de données grace au code et au type de celle-ci 
	 * et construit la SheetView correspondante.
	 * 
	 * @param code code de la fiche
	 * @param type type de la fiche
	 * @return la SheetView construite
	 */
	public SheetView findSheet(String code, String type) {
		if (code == null) {
			return null;
		}
		
		/*
		if(!Configuration.getSheetTypes().contains(type)) {
			return null;
		}
		*/
		
		Sheet sheet = dao.findSheetByCode(code, type);
		if (sheet == null) {
			return null;
		}
		
		if (sheet.getStatus() == Status.Unknown) {
			sheetValidator.validate(sheet);
		}
		return generateSheetView(sheet);
	}
	
	/**
	 * Recherche des fiches dans la base de données par rapport à une propriété.
	 * 
	 * @param propertyName nom de la propriété
	 * @param propertyValue valeur de la propriété
	 * @param type type de fiche recherché
	 * @return liste des fiches trouvées 
	 */
	public List<Sheet> findSheetsByPropertyLike(String propertyName, String propertyValue, String type){
		return dao.findSheetsByPropertyLike(propertyName, propertyValue, type);
	}
	
	/**
	 * Teste si la personne possède les droits en édition sur la fiche.
	 * 
	 * @param personCode code de la personne
	 * @param sheetCode code de la fiche
	 * @return true si la personne à les droit false sinon
	 */
	public boolean personIsAllowedToModifySheet(String personCode, String sheetCode) {
		if(dao.personOwnsSheet(personCode, sheetCode) ||
		   dao.personHasDelegationForSheet(personCode, sheetCode)) {
			return true;
		}
		
		return false;
	}


	/**
	 * Recherche toutes les fiches d'un type donné dans la base de données
	 * et construction des SheetView correspondantes.
	 * 
	 * @param type type de fiche
	 * @return la SheetView construite
	 */
	public List<SheetView> findAllSheets(String type) {
		List<Sheet> sheets = dao.findAllSheets(type);
		List<SheetView> sheetViews = new ArrayList<SheetView>(sheets.size());
		for(Sheet sheet : sheets) {
			sheetViews.add(generateSheetView(sheet));
		}
		return sheetViews;
	}



	
	/*
	public SheetView findSheet_Bouchon(String code, String type) {
		Sheet sheet = new Sheet();
		sheet.setProperty(NamingUtil.CODE, "Albator");
		sheet.setProperty("label", "Mention");
		sheet.setProperty(NamingUtil.TYPE, "MENTION");
		sheet.setProperty("MEN_INT_REG", "Informatique");
		sheet.setProperty("MEN_PAG_WEB", "www.dil.univ-mrs.fr/");
		sheet.setProperty("MEN_NBR_CRE", "60");
		sheet.setProperty("MEN_DBO_PUB", "Aucune désolé...");
		sheet.setProperty("MEN_DES_CXP", "Les calanques, la pinèdes, les collines...");
		sheet.setProperty("MEN_OUV_INP", "Si tu es gentil!");
		
		return generateSheetView(sheet);
	}
	*/
}
